package org.jackysoft.security.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.stereotype.Component;

@Component("securityMetadataSource")
public class MySQLFilterInvocationSecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource {

	private  MySQLResourceCache resourceCache;
	static final Log log = LogFactory
			.getLog(MySQLFilterInvocationSecurityMetadataSource.class);

	/**
	 * 返回 url=>roles 映射!
	 * */
	protected  Map<String, Set<String>> findResources() {

		Map<String, Set<String>> resourceMap = new LinkedHashMap<String, Set<String>>();

		for (Resource resource : resourceCache.getRequestList()) {
			String url = resource.getUrl();
			String role = resource.getRole();
			Set<String> roleSet = resourceMap.get(url);
			if (roleSet == null) {
				roleSet = new HashSet<String>();
				resourceMap.put(url, roleSet);
			}
			roleSet.add(role);
		}

		return resourceMap;
	}

	protected  LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> buildRequestMap() {
		LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();

		Map<String, Set<String>> resourceMap = findResources();

		for (String url : resourceMap.keySet()) {
			RequestMatcher matcher = new ExtAntPathRequestMatcher(url);
			Set<String> roleSet = resourceMap.get(url);
			for (String role : roleSet) {
				SecurityConfig sc = new SecurityConfig(role);

				Collection<ConfigAttribute> atts = requestMap.get(matcher);
				if (atts == null) {
					atts = new ArrayList<ConfigAttribute>();
					requestMap.put(matcher, atts);
				}

				atts.add(sc);
			}
		}
		return requestMap;
	}

	@javax.annotation.Resource
	public void setResourceCache(MySQLResourceCache resourceCache) {
		this.resourceCache = resourceCache;
	}

	private  Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

	// ~ Constructors
	// ===================================================================================================

	
	/**
	 * Sets the internal request map from the supplied map. The key elements
	 * should be of type {@link RequestMatcher}, which. The path stored in the
	 * key will depend on the type of the supplied UrlMatcher.
	 * 
	 * @param requestMap
	 *            order-preserving map of request definitions to attribute lists
	 */
	public MySQLFilterInvocationSecurityMetadataSource(
			LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {

		//this.requestMap = requestMap;
	}

	
	// ~ Methods
	// ========================================================================================================

	public MySQLFilterInvocationSecurityMetadataSource() {
		//this(null);		
	}
	
	
	@PostConstruct
	public void init(){
		this.requestMap = buildRequestMap();
	}

	public Collection<ConfigAttribute> getAllConfigAttributes() {
		Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();

		for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
				.entrySet()) {
			allAttributes.addAll(entry.getValue());
		}

		return allAttributes;
	}

	public Collection<ConfigAttribute> getAttributes(Object object) {
		final HttpServletRequest request = ((FilterInvocation) object)
				.getRequest();
		for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
				.entrySet()) {
			RequestMatcher key = entry.getKey();
			if (key.matches(request)) {
				Collection<ConfigAttribute> atts = entry.getValue();
				return atts;
			}
		}
		return null;
	}

	public boolean supports(Class<?> clazz) {
		return FilterInvocation.class.isAssignableFrom(clazz);
	}
	
	

}
